package com.microtf.inscription.metaweblog;

import com.microtf.inscription.metaweblog.vo.Blog;
import com.microtf.inscription.metaweblog.vo.Category;
import com.microtf.inscription.metaweblog.vo.FileData;
import com.microtf.inscription.metaweblog.vo.Post;
import com.microtf.inscription.metaweblog.vo.User;
import com.microtf.inscription.metaweblog.xmlrpc.RpcController;
import com.microtf.inscription.metaweblog.xmlrpc.StreamConfig;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.parser.XmlRpcRequestParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * MetaWebLog接口实现
 *
 * @author guliuzhong
 * @see XmlRpcService
 * @since 1.0
 */
public class MetaWebLogService extends XmlRpcService {
    Logger logger= LoggerFactory.getLogger(MetaWebLogService.class);
    /**
     * 不需要认证就能调用的方法
     */
    public static final List<String> NO_LOGIN_METHOD = Stream.of("system.listMethods").collect(Collectors.toList());
    /**
     * 标题
     * 为满足规范而建立常量
     */
    public static final String TITLE = "title";
    /**
     * 描述
     * 为满足规范而建立常量
     */
    public static final String DESCRIPTION = "description";
    /**
     * 描述
     * 为满足规范而建立常量
     */
    public static final String DATE_CREATED = "dateCreated";
    /**
     * 类目
     * 为满足规范而建立常量
     */
    public static final String CATEGORIES = "categories";
    /**
     * 文章ID
     * 为满足规范而建立常量
     */
    public static final String POSTID = "postid";
    /**
     * 链接
     * 为满足规范而建立常量
     */
    public static final String LINK = "link";
    /**
     * 分类ID
     * 为满足规范而建立常量
     */
    public static final String CATEGORYID = "categoryid";
    /**
     * rssUrl
     * 为满足规范而建立常量
     */
    public static final String RSS_URL = "rssUrl";
    /**
     * htmlUrl
     * 为满足规范而建立常量
     */
    public static final String HTML_URL = "htmlUrl";
    public static final String BLOGGER_DELETE_POST = "blogger.deletePost";
    final Map<String, String> methodMap = new HashMap<>();
    /**
     * rpc url地址
     */
    String rpcUrl;
    /**
     * rpc url地址
     */
    String rpcHost;

    public String getRpcUrl() {
        return rpcUrl;
    }

    public void setRpcUrl(String rpcUrl) {
        this.rpcUrl = rpcUrl;
    }

    public String getRpcHost() {
        return rpcHost;
    }

    public void setRpcHost(String rpcHost) {
        this.rpcHost = rpcHost;
    }

    /**
     * 服务接口回调监听
     */
    private MetaWebLogListener metaWebLogListener = new MetaWebLogListener() {
    };

    public void setMetaWebLogListener(MetaWebLogListener metaWebLogListener) {
        this.metaWebLogListener = metaWebLogListener;
    }

    /**
     * 服务初始化
     * 将metaWebLog的服务名和本类的方法做映射
     */
    public MetaWebLogService() {
        setRpcController(new RpcController());
        setStreamConfig(new StreamConfig());
        methodMap.put("blogger.getUsersBlogs", "getUsersBlogs");
        methodMap.put("wp.getUsersBlogs", "getUsersBlogs");
        methodMap.put(BLOGGER_DELETE_POST, "deletePost");
        methodMap.put("metaWeblog.getCategories", "getCategories");
        methodMap.put("metaWeblog.getRecentPosts", "getRecentPosts");
        methodMap.put("metaWeblog.newPost", "newPost");
        methodMap.put("metaWeblog.editPost", "editPost");
        methodMap.put("metaWeblog.getPost", "getPost");
        methodMap.put("metaWeblog.newMediaObject", "newMediaObject");
        methodMap.put("system.listMethods", "listMethods");
    }

    /**
     * 获取rsd内容
     *
     * @return rsd内容
     */
    public String getRsd() {
        return "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
                "<rsd version=\"1.0\" xmlns=\"http://archipelago.phrasewise.com/rsd\">\n" +
                "\t<service>\n" +
                "\t\t<engineName>meta web log</engineName>\n" +
                "\t\t<engineLink>http://microtf.com/</engineLink>\n" +
                "\t\t<homePageLink>http://microtf.com/</homePageLink>\n" +
                "\t\t<apis>\n" +
                "\t\t\t<api name=\"MetaWeblog\" blogID=\"1\" preferred=\"true\" apiLink=\"" + (rpcHost != null ? rpcHost : "") + rpcUrl + "\"/>\n" +
                "\t\t</apis>\n" +
                "\t</service>\n" +
                "</rsd>";
    }

    /**
     * 服务执行入口
     *
     * @param inputStream  客户端输入流
     * @param outputStream 客户端输出流
     */
    public void invoke(InputStream inputStream, OutputStream outputStream) {
        try {
            XmlRpcRequestParser xmlRequestParser;
            if(logger.isDebugEnabled()){
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                byte[] readByte = new byte[1024];
                int read;
                while ((read = inputStream.read(readByte, 0, 1024)) > 0) {
                    byteArrayOutputStream.write(readByte, 0, read);
                }
                logger.debug(byteArrayOutputStream.toString());
                ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                xmlRequestParser = getXmlRequestParser(byteArrayInputStream);
            }else{
                xmlRequestParser = getXmlRequestParser(inputStream);
            }
            String methodName = xmlRequestParser.getMethodName();
            if (metaWebLogListener.getEnable()) {
                String classMethod = methodMap.get(methodName);
                if (classMethod != null) {
                    try {
                        if (NO_LOGIN_METHOD.stream().noneMatch(item -> item.equals(methodName))) {
                            @SuppressWarnings("rawtypes")
                            List params = xmlRequestParser.getParams();
                            if (params == null) {
                                responseError(outputStream, 403, "无权限调用");
                            }
                            if (params != null) {
                                String userName = xmlRequestParser.getParams().get(1).toString();
                                String password = xmlRequestParser.getParams().get(2).toString();
                                if (BLOGGER_DELETE_POST.equals(methodName)) {
                                    userName = xmlRequestParser.getParams().get(2).toString();
                                    password = xmlRequestParser.getParams().get(3).toString();
                                }
                                assert metaWebLogListener != null;
                                Optional<User> login = metaWebLogListener.login(userName, password);
                                if (!login.isPresent()) {
                                    responseError(outputStream, 403, "无权限执行");
                                    return;
                                }
                                Method method = this.getClass().getDeclaredMethod(classMethod, XmlRpcRequestParser.class, User.class);
                                Object invoke = method.invoke(this, xmlRequestParser, login.get());
                                response(outputStream, invoke);
                            }
                        } else {
                            Method method = this.getClass().getDeclaredMethod(classMethod, XmlRpcRequestParser.class);
                            Object invoke = method.invoke(this, xmlRequestParser);
                            response(outputStream, invoke);
                        }
                    } catch (NoSuchMethodException e) {
                        responseError(outputStream, 404, "服务不存在");
                    } catch (IllegalAccessException e) {
                        responseError(outputStream, 500, "服务执行出错 IllegalAccessException");
                    } catch (InvocationTargetException e) {
                        Throwable t = e.getCause();
                        do {
                            if (t instanceof ConstraintViolationException) {
                                Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) t).getConstraintViolations();
                                logger.error(constraintViolations.toString());
                                Map<String, String> collect = constraintViolations.stream().collect(Collectors.toMap(k -> k.getPropertyPath().toString(), ConstraintViolation::getMessage, (v1, v2) -> v1 + "," + v2));
                                responseError(outputStream, 500, "服务执行出错" + collect.toString());
                                return;
                            }
                        } while ((t = t.getCause()) != null);
                        responseError(outputStream, 500, "服务执行出错 InvocationTargetException");
                    }
                } else {
                    responseError(outputStream, 404, "服务不存在");
                }
            } else {
                responseError(outputStream, 403, "MetaWebLog发布服务未开启，请联系管理员处理。");
            }
        } catch (XmlRpcException | IOException | SAXException e) {
            responseError(outputStream, 400, "解析数据出错,服务数据出错");
            logger.error("解析数据出错,服务数据出错{}", e.getMessage());
        }
    }

    /**
     * 获取系统支持的方法
     *
     * @param xmlRpcRequestParser 输入入参
     * @return 返回方法列表
     */
    @SuppressWarnings("unused")
    private List<String> listMethods(@SuppressWarnings("unused") XmlRpcRequestParser xmlRpcRequestParser) {
        return new ArrayList<>(methodMap.keySet());
    }

    /**
     * 获取用户博客
     *
     * @param xmlRpcRequestParser 输入入参
     * @param user                用户信息
     * @return 博客列表
     */
    @SuppressWarnings("unused")
    private ArrayList<Map<String, String>> getUsersBlogs(@SuppressWarnings("unused") XmlRpcRequestParser xmlRpcRequestParser, User user) {
        ArrayList<Map<String, String>> mapVector = new ArrayList<>();
        List<Blog> blogs = metaWebLogListener.getBlogs(user);
        for (Blog blog : blogs) {
            Map<String, String> userBlog = new HashMap<>(16);
            userBlog.put("blogid", blog.getBlogId());
            userBlog.put("blogName", blog.getBlogName());
            userBlog.put("url", blog.getUrl());
            mapVector.add(userBlog);
        }
        return mapVector;
    }

    /**
     * 获取分类
     *
     * @param xmlRequestParser 输入入参
     * @param user             用户信息
     * @return 分类列表
     */
    @SuppressWarnings("unused")
    private ArrayList<Map<String, String>> getCategories(XmlRpcRequestParser xmlRequestParser, User user) {
        assert metaWebLogListener != null;
        List<Category> category = metaWebLogListener.getCategory(Blog.builder().blogId(xmlRequestParser.getParams().get(0).toString()).build(), user);
        return category.stream().map(item -> {
            Map<String, String> template = new HashMap<>(16);
            template.put(TITLE, item.getTitle());
            template.put(CATEGORYID, item.getCategoryId());
            template.put(DESCRIPTION, item.getDescription());
            template.put(RSS_URL, item.getRssUrl());
            template.put(HTML_URL, item.getHtmlUrl());
            return template;
        }).collect(Collectors.toCollection(ArrayList::new));
    }

    /**
     * 上传附件
     *
     * @param xmlRequestParser 输入入参
     * @param user             用户信息
     * @return 上传结果返回值是一个数组
     */
    @SuppressWarnings("unused")
    private Map<String, String> newMediaObject(XmlRpcRequestParser xmlRequestParser, User user) {
        assert metaWebLogListener != null;
        @SuppressWarnings("unchecked")
        Map<String, Object> stringObjectMap = (Map<String, Object>) xmlRequestParser.getParams().get(3);
        Map<String, String> mediaObject = new HashMap<>(16);
        byte[] bits = (byte[]) stringObjectMap.get("bits");
        FileData fileData = new FileData();
        fileData.setBase64Bits(bits);
        fileData.setName((String) stringObjectMap.get("name"));
        fileData.setType((String) stringObjectMap.get("type"));
        String s1 = metaWebLogListener.newMediaObject(Blog.builder().blogId(xmlRequestParser.getParams().get(0).toString()).build(), user, fileData);
        mediaObject.put("url", s1);
        return mediaObject;
    }

    /**
     * 删除文章内容
     *
     * @param xmlRequestParser 输入入参
     * @param user             用户信息
     * @return 删除文章内容
     */
    @SuppressWarnings("unused")
    Boolean deletePost(XmlRpcRequestParser xmlRequestParser, User user) {
        assert metaWebLogListener != null;
        metaWebLogListener.deletePost(xmlRequestParser.getParams().get(1).toString(), user);
        return true;
    }

    /**
     * 获取文章信息
     *
     * @param xmlRequestParser 输入入参
     * @param user             用户信息
     * @return 文章信息
     */
    @SuppressWarnings("unused")
    private Map<String, Object> getPost(XmlRpcRequestParser xmlRequestParser, User user) {
        assert metaWebLogListener != null;
        Map<String, Object> postMap = new HashMap<>(16);
        String postId = xmlRequestParser.getParams().get(0).toString();
        Optional<Post> post = metaWebLogListener.getPost(postId, user);
        post.ifPresent(post1 -> {
            postMap.put(DATE_CREATED, post1.getDateCreated() == null ? new Date() : post1.getDateCreated());
            postMap.put(TITLE, post1.getTitle());
            postMap.put(POSTID, post1.getPostId());
            postMap.put(DESCRIPTION, post1.getDescription());
            postMap.put(LINK, post1.getLink());
            postMap.put(CATEGORIES,post1.getCategories());
        });
        return postMap;
    }

    /**
     * 获取近期发送内容列表
     *
     * @param xmlRequestParser 输入入参
     * @param user             用户信息
     * @return 近期发送的内容列表
     */
    @SuppressWarnings("unused")
    ArrayList<Map<String, Object>> getRecentPosts(XmlRpcRequestParser xmlRequestParser, User user) {
        int pageSize = Integer.parseInt(xmlRequestParser.getParams().get(3).toString());
        pageSize = Math.min(pageSize, 100);
        pageSize = Math.max(0, pageSize);
        assert metaWebLogListener != null;
        Blog blog = Blog.builder().blogId(xmlRequestParser.getParams().get(0).toString()).build();
        List<Post> recentPosts = metaWebLogListener.getRecentPosts(blog, pageSize, user);
        return recentPosts.stream().map(item -> {
            Map<String, Object> post = new HashMap<>(16);
            post.put(DATE_CREATED, item.getDateCreated() != null ? item.getDateCreated() : new Date());
            post.put(TITLE, item.getTitle());
            post.put(POSTID, item.getPostId());
            post.put(DESCRIPTION, item.getDescription());
            post.put(LINK, item.getLink());
            post.put(CATEGORIES,item.getCategories());
            return post;
        }).collect(Collectors.toCollection(ArrayList::new));

    }

    /**
     * 编辑内容
     *
     * @param xmlRequestParser 输入入参
     * @param user             用户信息
     * @return 编辑内容
     */
    @SuppressWarnings("unused")
    String editPost(XmlRpcRequestParser xmlRequestParser, User user) {
        @SuppressWarnings("unchecked")
        HashMap<String, Object> stringObjectHashMap = (HashMap<String, Object>) xmlRequestParser.getParams().get(3);
        Post post = new Post();
        post.setPostId(xmlRequestParser.getParams().get(0).toString());
        post.setTitle(stringObjectHashMap.get(TITLE).toString());
        post.setDescription(stringObjectHashMap.get(DESCRIPTION).toString());
        if (stringObjectHashMap.get(DATE_CREATED) != null) {
            post.setDateCreated((Date) stringObjectHashMap.get(DATE_CREATED));
        }
        if (stringObjectHashMap.get(CATEGORIES) != null) {
            Object[] categories = (Object[]) stringObjectHashMap.get(CATEGORIES);
            post.setCategories(Stream.of(categories).map(item -> (String) item).toArray(String[]::new));
        }
        post.setPublish((Boolean) xmlRequestParser.getParams().get(4));
        post.setUser(user);
        metaWebLogListener.editPost(null, post);
        return "";
    }

    /**
     * 编辑新内容
     *
     * @param xmlRequestParser 输入入参
     * @param user             用户信息
     * @return 编辑后的ID postId
     */
    @SuppressWarnings("unused")
    String newPost(XmlRpcRequestParser xmlRequestParser, User user) {
        @SuppressWarnings("unchecked")
        HashMap<String, Object> stringObjectHashMap = (HashMap<String, Object>) xmlRequestParser.getParams().get(3);
        Post post = new Post();
        post.setTitle(stringObjectHashMap.get(TITLE).toString());
        post.setDescription(stringObjectHashMap.get(DESCRIPTION).toString());
        if (stringObjectHashMap.get(DATE_CREATED) != null) {
            post.setDateCreated((Date) stringObjectHashMap.get(DATE_CREATED));
        }
        if (stringObjectHashMap.get(CATEGORIES) != null) {
            Object[] categories = (Object[]) stringObjectHashMap.get(CATEGORIES);
            post.setCategories(Stream.of(categories).map(item -> (String) item).toArray(String[]::new));
        }
        post.setUser(user);
        post.setPublish((Boolean) xmlRequestParser.getParams().get(4));
        Blog blog = Blog.builder().blogId(xmlRequestParser.getParams().get(0).toString()).build();
        Post post1 = metaWebLogListener.editPost(blog, post);
        return post1.getPostId();
    }
}
